Skip to content

[DRAFT] Managed Harness Agents#9080

Open
kshitij-microsoft wants to merge 14 commits into
Azure:mainfrom
kshitij-microsoft:kchawla/azd-managed-harness
Open

[DRAFT] Managed Harness Agents#9080
kshitij-microsoft wants to merge 14 commits into
Azure:mainfrom
kshitij-microsoft:kchawla/azd-managed-harness

Conversation

@kshitij-microsoft

Copy link
Copy Markdown
Member

No description provided.

Copilot AI review requested due to automatic review settings July 10, 2026 10:38
@github-actions github-actions Bot added ext-agents azure.ai.agents extension ext-x microsoft.azd.extensions developer extension (azd x) labels Jul 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot wasn't able to review this pull request because it exceeds the maximum diff size. Try reducing the number of changed files and lines, and requesting a review from Copilot again.

@jongio jongio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few things to sort out before this leaves draft.

Committed artifacts that look accidental:

  • docs/specs/managed-harness-agents/~$naged-agents-getting-started.docx is a Word lock/owner temp file. It shouldn't be tracked. Delete it and add ~$* to .gitignore.
  • cli/azd/extensions/azure.ai.agents/my-prompt-agent-0701-02/ looks like generated scratch output: timestamped folder name, a full infra tree including a 1236-line generated applicationinsights-dashboard.bicep. Is this meant to ship, or is it leftover from a local run? If it's scratch, drop the whole directory.
  • spec.docx and managed-agents-getting-started.docx are binaries committed next to their Python generators. Do you want the binaries tracked, or just the generators plus the .md?

Registry:

  • The new azure.ai.agents entry points every artifact URL at github.com/kshitij-microsoft/azure-dev/releases/... (a personal fork). Every other entry in this file uses github.com/Azure/azure-dev/releases/.... This needs to point at official releases before merge, otherwise azd resolves this extension from a fork.

PR hygiene:

  • The description is empty and the title is [DRAFT] but the PR isn't marked as a draft. Add a description and flip it to draft if it isn't ready for review.

// ResolvePromptTargetFromEnv applies azd environment-derived overrides to the
// prompt settings so both deploy and the lifecycle commands (show/invoke/list/
// delete) target the same managed agent route.
//

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This recover only logs and then re-panics, so the panic still tears down the extension process instead of returning an error to azd. Either return an error here (deploy handlers are expected to) or drop the recover, since as written it doesn't change the outcome.

Copilot AI review requested due to automatic review settings July 10, 2026 11:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 56 out of 56 changed files in this pull request and generated 7 comments.

Comment on lines +157 to +159
if _, err := client.GetByID(ctx, resourceID, storageAPIVersion, nil); err == nil {
return resourceID, nil // already exists
}
Comment on lines +195 to +197
if _, err := client.GetByID(ctx, resourceID, keyVaultAPIVersion, nil); err == nil {
return resourceID, nil // already exists
}
Comment on lines +81 to +88
func (provisionedDeploymentResolver) Create(_ context.Context, modelName string) error {
// Should not be reached given Exists always returns true; guard defensively
// with an actionable message rather than a silent no-op.
fmt.Fprintf(os.Stderr,
"Model deployment %q was not found. Provision it with `azd provision` "+
"(deployments are declared in azure.yaml).\n", modelName)
return nil
}
Comment on lines +131 to +137
return connActionFailFast, resolved, exterrors.Validation(
exterrors.CodeInvalidAgentManifest,
fmt.Sprintf(
"connection %q has no existing connection and no resolvable target", decl.Name,
),
"set connections["+decl.Name+"].target, or set provision: true to create the backing resource",
)
Comment on lines +218 to +225
// Prompt (kind=managed) agents target the managed harness, not an ARM
// Foundry project. They self-authenticate via the harness client and carry
// their entire deploy target in the service config, so skip the
// subscription/tenant/credential resolution the hosted path needs.
if serviceIsPromptAgent(serviceConfig) {
fmt.Fprintf(os.Stderr, "Project path: %s, Service path: %s\n", proj.Project.Path, fullPath)
return p.resolveAgentDefinitionPath(proj.Project.Path, servicePath, fullPath)
}
@@ -1,5 +1,16 @@
# Release History

## Unreleased
Comment on lines 384 to +422
@@ -342,6 +396,31 @@ func postdownHandler(ctx context.Context, azdClient *azdext.AzdClient, args *azd
return nil
}

// deletePromptAgentOnDown best-effort deletes a prompt agent from the harness
// during `azd down`. Failures are logged, never returned — teardown of the
// project should not be blocked by a harness hiccup.
func deletePromptAgentOnDown(
ctx context.Context,
svc *azdext.ServiceConfig,
settings *project.PromptAgentSettings,
) {
settings.ApplyEnvOverrides()
if err := settings.Validate(); err != nil {
log.Printf("postdown: skipping harness delete for %q: %v", svc.Name, err)
return
}
client, err := project.NewPromptAgentClient(settings)
if err != nil {
log.Printf("postdown: failed to build harness client for %q: %v", svc.Name, err)
return
}
if _, err := client.DeleteAgent(ctx, svc.Name, settings.EffectiveAPIVersion(), true); err != nil {
log.Printf("postdown: failed to delete prompt agent %q from harness: %v", svc.Name, err)
return
}
fmt.Printf("Deleted prompt agent %q from the harness\n", svc.Name)
}

@jongio jongio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up after the latest commit. The accidental scratch artifacts (the my-prompt-agent-0701-02/ tree, the ~$ Word lock file, and the committed .docx binaries) are gone now. Two items from my earlier review are still open.

The registry.json entry for azure.ai.agents still points every artifact URL at github.com/kshitij-microsoft/azure-dev/releases/... (9 URLs). Every other entry in that file uses github.com/Azure/azure-dev/releases/.... This has to point at official Azure/azure-dev releases before merge, otherwise azd resolves this extension from a personal fork.

The PR is still titled [DRAFT] with an empty description but isn't marked as a draft. Add a description and flip it to draft until it's ready for review.

@jongio jongio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went deeper on the new Go code this pass. Two correctness issues.

One is inline on prompt_skills.go.

The other is in parseAgentEndpoint (cli/azd/extensions/azure.ai.agents/internal/cmd/agent_endpoint.go, around line 167). The new bypass path lets a dev pass http://localhost:5000 (scheme relaxed, port allowed), but the endpoint is then rebuilt as fmt.Sprintf("https://%s/api/projects/%s", host, projectSegment) where host is u.Hostname(). That forces the scheme back to https and drops the port, so an override like http://localhost:5000 resolves to https://localhost/... and requests never reach the local backend the override is meant to target. Preserve u.Scheme and u.Host (host+port) in the bypass case, the way validateProjectEndpoint does in project_endpoint.go.

// The body starts after the closing `---` line.
after := rest[end+len("\n---"):]
after = strings.TrimPrefix(after, "-") // tolerate longer --- fences
after = strings.TrimLeft(after, "-\r\n") // consume the rest of the fence line

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TrimLeft(after, "-\r\n") crosses the newline after the closing --- and keeps consuming -, so when a SKILL.md body's first line starts with a dash (a - bullet list, or a --- break) the leading dash is stripped from the instructions: - first bullet becomes first bullet. That mangled body flows into skillMeta.Instructions and ships to CreateSkillVersion. Trim only the remainder of the fence line (cut to the next \n) instead of a cut set that mixes - with newlines.

Copilot AI review requested due to automatic review settings July 15, 2026 13:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 58 out of 58 changed files in this pull request and generated 21 comments.

Comment on lines +104 to +105
if !strings.EqualFold(u.Scheme, "https") &&
!(bypass && strings.EqualFold(u.Scheme, "http")) {
// DefaultPromptModelEndpoint is the model gateway the harness calls to reach
// the LLM. It is sent on invoke (Responses) requests via the x-model-endpoint
// header.
const DefaultPromptModelEndpoint = "https://va-dev-fdp-resource.services.ai.azure.com"
Comment on lines +157 to +159
if _, err := client.GetByID(ctx, resourceID, storageAPIVersion, nil); err == nil {
return resourceID, nil // already exists
}
Comment on lines +195 to +197
if _, err := client.GetByID(ctx, resourceID, keyVaultAPIVersion, nil); err == nil {
return resourceID, nil // already exists
}
Comment on lines +183 to +184
reuse, _ := g.bindings[vectorStoreBindingKey].(string)
storeID, err := builder.EnsureVectorStore(ctx, g.managed.Name, reuse, files)
@@ -1,5 +1,17 @@
# Release History

## Unreleased
Comment on lines +19 to +23
// AgentKindPrompt is the Foundry "prompt" agent kind backed by the
// Prompt Execution Service (PES) Brain+Hand sandbox architecture.
// Lifecycle and response APIs live behind the same data-plane routes
// as the other Foundry kinds, with a "kind": "prompt" discriminator.
AgentKindPrompt AgentKind = "prompt"
Comment on lines +48 to +50
PerCallPolicies: []policy.Policy{
runtime.NewBearerTokenPolicy(cred, []string{"https://ai.azure.com/.default"}, nil),
azsdk.NewMsCorrelationPolicy(),
Comment on lines +47 to +49
PerCallPolicies: []policy.Policy{
runtime.NewBearerTokenPolicy(cred, []string{"https://ai.azure.com/.default"}, nil),
azsdk.NewMsCorrelationPolicy(),
return fmt.Errorf("marshaling request: %w", err)
}

req, err := runtime.NewRequest(ctx, http.MethodPost, targetURL)

@jongio jongio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at HEAD e991e5f. This commit (the ManagedAgent to PromptAgent rename plus the create-or-update version fallback) doesn't touch the items from my earlier reviews, and they're all still open:

  • registry.json points every azure.ai.agents artifact URL at github.com/kshitij-microsoft/azure-dev/releases/... instead of Azure/azure-dev, so azd would resolve this extension from a personal fork.
  • parseAgentEndpoint (cli/azd/extensions/azure.ai.agents/internal/cmd/agent_endpoint.go, around line 167) still rebuilds the endpoint as https://{u.Hostname()}/api/projects/..., which forces the scheme back to https and drops the port. An http://localhost:5000 override resolves to https://localhost/... and never reaches the local backend it's meant to target.
  • My inline comments on service_target_prompt.go (the recover that logs and then re-panics) and prompt_skills.go (TrimLeft(after, "-\r\n") crossing the closing-fence newline and stripping a leading dash from the skill body) are unchanged.

Still titled [DRAFT] with an empty description and not marked as a draft.

Copilot AI review requested due to automatic review settings July 15, 2026 13:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 58 out of 58 changed files in this pull request and generated 22 comments.

// DefaultPromptModelEndpoint is the model gateway the harness calls to reach
// the LLM. It is sent on invoke (Responses) requests via the x-model-endpoint
// header.
const DefaultPromptModelEndpoint = "https://va-dev-fdp-resource.services.ai.azure.com"
Comment on lines +215 to +219
p, err := agent_api.BuildWorkspaceRoutePrefix(
settings.SubscriptionID, settings.ResourceGroup, settings.Workspace,
)
if err != nil {
return nil, fmt.Errorf("building workspace route prefix: %w", err)
Comment on lines +104 to +105
if !strings.EqualFold(u.Scheme, "https") &&
!(bypass && strings.EqualFold(u.Scheme, "http")) {
Comment on lines +77 to +78
if !strings.EqualFold(u.Scheme, "https") &&
!(bypass && strings.EqualFold(u.Scheme, "http")) {
Comment on lines +76 to +82
info, statErr := os.Stat(full)
if statErr != nil {
return nil, fmt.Errorf("stat %q: %w", full, statErr)
}
if info.IsDir() {
continue
}
Comment on lines +335 to +337
// Provision opts into the deploy engine creating the backing Azure resource
// (via an emitted Bicep module) when no existing connection or target can be
// resolved. Defaults to false (fail fast with guidance).
Comment on lines +94 to +98
func TestTargetFromEnv(t *testing.T) {
env := map[string]string{
"AI_PROJECT_DEPENDENT_RESOURCES": "foo=https://foo; conn=https://target",
}
if got := targetFromEnv("conn", env); got != "https://target" {
Comment on lines +979 to +983
hostedSignalsPresent := userProvidedManifest ||
flags.src != "" ||
flags.deployMode != "" ||
flags.runtime != "" ||
flags.entryPoint != ""
@@ -1,5 +1,17 @@
# Release History

## Unreleased
@@ -1 +1 @@
0.1.41-preview
0.1.44-preview

@jongio jongio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The two new commits bump the version to 0.1.44 and add a matching registry entry. That new entry re-adds the fork artifact URLs I flagged earlier (inline below). The other open items from my prior reviews aren't touched by these commits and are still open: the parseAgentEndpoint scheme/port drop in agent_endpoint.go, the recover-then-repanic in service_target_prompt.go, the TrimLeft in prompt_skills.go, and the [DRAFT] title plus empty description on a PR that isn't marked as a draft.

"value": "8cae1b35438b2a79fed0b0b29fd423bffa0ced33b399dddb64042ea0b76b1ff2"
},
"entryPoint": "azure-ai-agents-darwin-amd64",
"url": "https://github.com/kshitij-microsoft/azure-dev/releases/download/azd-ext-azure-ai-agents_0.1.44-preview/azure-ai-agents-darwin-amd64.zip"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new 0.1.44 entry points all six artifact URLs at github.com/kshitij-microsoft/azure-dev/releases/... (a personal fork), the same as the earlier entries I flagged. Every other extension in this file resolves from github.com/Azure/azure-dev/releases/... These need to point at official Azure/azure-dev releases before merge, otherwise azd resolves this extension from a fork.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 59 out of 59 changed files in this pull request and generated 18 comments.

Comment on lines +104 to +105
if !strings.EqualFold(u.Scheme, "https") &&
!(bypass && strings.EqualFold(u.Scheme, "http")) {
Comment on lines +229 to +233
if strings.TrimSpace(reuseStoreID) != "" {
return reuseStoreID, nil
}

store, err := b.client.CreateVectorStore(ctx, name, fileIDs)
Comment on lines +335 to +338
// Provision opts into the deploy engine creating the backing Azure resource
// (via an emitted Bicep module) when no existing connection or target can be
// resolved. Defaults to false (fail fast with guidance).
Provision bool `json:"provision,omitempty" yaml:"provision,omitempty"`
Comment on lines +328 to +330
// Credentials carries auth material for non-Entra auth (e.g. an API key,
// possibly as a ${ENV_VAR} reference resolved at deploy time).
Credentials map[string]any `json:"credentials,omitempty" yaml:"credentials,omitempty"`
Comment on lines +77 to +78
func (provisionedDeploymentResolver) Exists(context.Context, string) (bool, error) {
return true, nil
Comment on lines +75 to +82
full := filepath.Join(dir, name)
info, statErr := os.Stat(full)
if statErr != nil {
return nil, fmt.Errorf("stat %q: %w", full, statErr)
}
if info.IsDir() {
continue
}
fmt.Fprint(w, payload.Delta)
wroteText = true
}
} else if strings.HasPrefix(event, "response.") {
Comment on lines +439 to +443
if p.isPromptAgentService() {
settings, err := p.promptAgentSettings()
if err != nil {
return nil, err
}
Comment on lines +384 to +388
// Prompt (kind=managed) agents are removed from the harness on down so
// `azd down` fully tears down the agent alongside the infrastructure.
// Best-effort: a harness failure is logged but does not block down.
if settings, isPrompt := promptSettingsFromService(svc); isPrompt {
deletePromptAgentOnDown(ctx, svc, settings)
Comment on lines +362 to +366
if p.isPromptAgentService() {
settings, err := p.promptAgentSettings()
if err != nil {
return nil, err
}

@jongio jongio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at HEAD 9d92a95. I went through the four new commits (multi-turn memory via previous_response_id, the skill-bundle multipart upload, dropping the files/ scaffold, and the 0.1.45 version bump). No new correctness issues in the Go changes. The bundle change closes the gap where only SKILL.md was uploaded, and the added tests cover nested references/, assets/, and scripts/ files.

The new 0.1.45 registry entry re-adds the fork artifact URLs (inline below), so the registry problem I flagged earlier is back on this entry.

Open items from my prior reviews that these commits don't touch:

  • parseAgentEndpoint in agent_endpoint.go still rebuilds the endpoint as https://{u.Hostname()}/api/projects/..., which forces the scheme back to https and drops the port, so an http://localhost:5000 override resolves to https://localhost/... and never reaches the local backend.
  • The recover in service_target_prompt.go still logs and then re-panics instead of returning an error to azd.
  • The TrimLeft(after, "-\r\n") in prompt_skills.go extractFrontmatter is still there. Its impact is smaller now: after the bundle-upload refactor the parsed body is only used for the empty-instructions check, not uploaded, so a stripped leading dash no longer corrupts what the service stores. Still worth fixing so the validation sees the real body.

Still titled [DRAFT] with an empty description and not marked as a draft.

"value": "38e1e008a153296a40c956c77546039e3a551e4e8c211e1f43ec72058ee6c516"
},
"entryPoint": "azure-ai-agents-darwin-amd64",
"url": "https://github.com/kshitij-microsoft/azure-dev/releases/download/azd-ext-azure-ai-agents_0.1.45-preview/azure-ai-agents-darwin-amd64.zip"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new 0.1.45 entry points all six artifact URLs at github.com/kshitij-microsoft/azure-dev/releases/... (a personal fork), same as the earlier entries. Every other extension in this file resolves from github.com/Azure/azure-dev/releases/... This has to point at official Azure/azure-dev releases before merge, otherwise azd resolves this extension from a fork.

Copilot AI review requested due to automatic review settings July 20, 2026 17:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 61 out of 61 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (13)

cli/azd/extensions/azure.ai.agents/internal/project/prompt_files.go:83

  • azd-code-reviewer: os.Stat and os.ReadFile both follow symlinks. A repository can therefore place a symlink under files/ that points outside the project, causing azd deploy to upload a local file such as credentials. Use os.Lstat, reject symlinks, and read only regular files.
    cli/azd/extensions/azure.ai.agents/internal/project/prompt_files.go:213
  • azd-code-reviewer: The hash map exists only on this newly constructed builder, and each graph starts with an empty vector_store_id binding. Consequently every azd deploy uploads every file and creates another vector store; the advertised cross-deploy hash dedupe/reuse never occurs. Persist or discover the prior store and uploaded file hashes before resolving this node.
    cli/azd/extensions/azure.ai.agents/internal/project/prompt_deployment.go:78
  • azd-code-reviewer: The production resolver unconditionally reports that every model deployment exists, so the node's create-if-missing branch is unreachable. A manually authored service or an agent whose model changed after provisioning proceeds without creating or even detecting the missing deployment, despite the changelog promising create-if-missing behavior. Query the actual deployment or return a validation error when it is absent.
    cli/azd/extensions/azure.ai.agents/internal/cmd/list.go:92
  • azd-code-reviewer: This performs only one unfiltered page request. AgentList exposes HasMore/LastID, and the API accepts a kind filter, so a project with multiple kinds or more than one page will make this prompt-only command display non-prompt agents and omit later prompt agents. Request kind=prompt and follow pagination until HasMore is false.
	list, err := client.ListAgents(ctx, nil, pctx.Settings.EffectiveAPIVersion())

cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go:68

  • azd-code-reviewer: isHostedAgentService only reads agent.yaml, while the service target explicitly supports agent.yml and AGENT_DEFINITION_PATH. For a hosted service using either supported alternative, hostedAgentCount remains zero and this handler writes ENABLE_HOSTED_AGENTS=false, disabling required ACR/RBAC infrastructure. Count every non-prompt agent service as hosted, or reuse the service target's definition-path resolution.
			if isHostedAgentService(svc, args.Project) {
				hostedAgentCount++
			}

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke.go:370

  • azd-code-reviewer: Prompt-service errors are discarded here. Invalid prompt settings or a malformed project resource ID therefore fall through to hosted protocol resolution and produce an unrelated error instead of the actionable prompt error. A non-prompt service already returns (nil, false, nil), so return pErr whenever it is non-nil.
		pctx, isPrompt, pErr := resolvePromptAgentService(ctx, azdClient, a.flags.name, a.noPrompt)
		azdClient.Close()
		if pErr == nil && isPrompt {
			return a.runPromptInvoke(ctx, pctx)
		}
		// pErr (e.g. no azure.yaml) is non-fatal here: fall through to the
		// existing hosted/local resolution which surfaces its own errors.

cli/azd/extensions/azure.ai.agents/internal/cmd/delete.go:109

  • azd-code-reviewer: Prompt-service resolution errors are silently treated as “not prompt.” A malformed promptAgent target then enters the hosted delete path and reports an unrelated missing endpoint/name error. Return pErr first; only fall back when isPrompt is false with no error.
	if pctx, isPrompt, pErr := resolvePromptAgentService(
		ctx, azdClient, a.flags.name, a.flags.noPrompt,
	); pErr == nil && isPrompt {
		return a.runPromptDelete(ctx, azdClient, pctx)
	}

cli/azd/extensions/azure.ai.agents/internal/project/workspace_create.go:159

  • azd-code-reviewer: Every storage existence error is treated as “not found.” A 403, cancellation, or transient ARM failure consequently triggers a PUT and obscures the original failure. Only create after an azcore.ResponseError with status 404; return all other errors.
    cli/azd/extensions/azure.ai.agents/internal/project/workspace_create.go:197
  • azd-code-reviewer: Every key-vault lookup error is treated as “not found.” On authorization, cancellation, or transient failures this attempts an unnecessary create/update and loses the useful original context. Proceed only on a 404 response and return other errors.
    cli/azd/extensions/azure.ai.agents/CHANGELOG.md:3
  • azd-code-reviewer: Release tooling does not recognize a bare ## Unreleased; its parser requires a versioned ## <semver> (Unreleased) heading. Because this PR bumps the extension to 0.1.46-preview, this section will be skipped by changelog generation unless the heading includes that version.
## Unreleased

cli/azd/extensions/azure.ai.agents/internal/cmd/listen.go:418

  • azd-code-reviewer: Post-down cleanup uses the raw service name and un-overlaid settings, while deployment uses the name from the agent definition and resolves ProjectEndpoint/workspace from azd environment values. Services whose agent name differs, or no-prompt projects whose endpoint came from provisioning outputs, will delete the wrong route and leave the managed agent behind. Resolve the same deployed identity and target used by deploy before calling DeleteAgent.
	if _, err := client.DeleteAgent(ctx, svc.Name, settings.EffectiveAPIVersion(), true); err != nil {
		log.Printf("postdown: failed to delete prompt agent %q from harness: %v", svc.Name, err)

cli/azd/extensions/azure.ai.agents/internal/cmd/prompt_service.go:101

  • azd-code-reviewer: Lifecycle commands hardcode agent.yaml, although deployment supports agent.yml and AGENT_DEFINITION_PATH. For those valid layouts this silently falls back to the service name and leaves the model empty; show/delete can target the wrong agent and invoke sends an empty model. Resolve the definition with the same helper/rules used by the service target and surface read/parse errors instead of silently changing identity.
		if data, readErr := os.ReadFile(filepath.Join(pctx.ServiceDir, "agent.yaml")); readErr == nil {

cli/azd/extensions/azure.ai.agents/internal/cmd/invoke_managed.go:169

  • azd-code-reviewer: response.failed and error SSE events are consumed as successful lifecycle events. The command can print partial output, exit with status 0, and persist a response ID even though the harness reported failure. Parse these event payloads and return their error after the stream (or immediately) instead of treating every non-delta event as success.
			} else if strings.HasPrefix(event, "response.") {
				// Capture the response id from any lifecycle event that carries
				// it (e.g. response.created, response.completed). The last one
				// seen wins so the persisted id reflects the completed turn.
				var payload struct {

Comment on lines +224 to +228
return agent_api.NewManagedAgentClient(agent_api.ManagedAgentClientOptions{
BaseURL: baseURL,
RoutePrefix: prefix,
Credential: promptCredential(),
Scopes: promptScopesForBaseURL(baseURL),

@jongio jongio left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at HEAD b97ede5. The one new commit since my last pass (the 1.46-preview release) adds the toolbox project-connection wiring (ensureToolboxConnection / FoundryConnectionsARMClient, and injectMcpTool now carrying project_connection_id), the show.go harness/endpoint/toolbox output, and seeding AZURE_LOCATION from the project region. I went through the new Go in prompt_skills.go, foundry_connections_controlplane.go, show.go, and init_managed_foundry.go and didn't find new correctness issues there.

The open items from my earlier reviews are all still open:

  • registry.json: the new 0.1.46 entry re-adds the fork artifact URLs, so every azure.ai.agents URL from 0.1.42 through 0.1.46 (30 of them) points at github.com/kshitij-microsoft/azure-dev/releases/.... Every other entry in the file uses github.com/Azure/azure-dev/releases/.... These have to point at official Azure/azure-dev releases before merge, otherwise azd resolves this extension from a personal fork.

  • parseAgentEndpoint (agent_endpoint.go) still rebuilds the endpoint as https://{u.Hostname()}/api/projects/.... The new comment on the port check even says the override path allows http://localhost:5000 for local backends, but the rebuild drops the port and forces https, so http://localhost:5000 still resolves to https://localhost/... and never reaches the local backend it targets. Preserve u.Scheme and u.Host (host+port) in the bypass case, the way validateProjectEndpoint does.

  • service_target_prompt.go deployPromptAgent still recovers, logs the stack, then re-panics instead of returning an error to azd.

  • prompt_skills.go extractFrontmatter still does TrimLeft(after, "-\r\n"), which crosses the newline after the closing fence and strips a leading dash from the body when it starts with a list item. Smaller impact now that the parsed body only feeds the empty-instructions check, but still worth fixing so validation sees the real body.

Still titled [DRAFT] with an empty description and not marked as a draft. Add a description and flip it to draft until it's ready for review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ext-agents azure.ai.agents extension ext-x microsoft.azd.extensions developer extension (azd x)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants